package com.kinyx.framework.security;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import com.kinyx.framework.Configs;
import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.CanNull;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.annotation.assist.Tester;
import com.kinyx.framework.annotation.assist.Throws;
import com.kinyx.framework.business.KHexUtils;
import com.kinyx.framework.enums.impl.ErrorCodeX;
import com.kinyx.framework.exception.KRuntimeException;
import com.kinyx.framework.file.KReader;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * MD5加密工具类
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Tester("com.kinyx.framework.security.KMD5_Tester_")
public final class KMD5 {

	/**
	 * 获取MD5对象
	 * @return MD5对象
	 */
	@ReturnNotNull
	//	@Throws(at = "MD5.md5()", value = "NoSuchAlgorithmException", when = "MessageDigest.getInstance(String)")
	private static MessageDigest md5() {
		try {
			return MessageDigest.getInstance("MD5");
		} catch (final NoSuchAlgorithmException e) {// never happens
			throw new KRuntimeException(e, ErrorCodeX.NeverHappens);
		}
	}

	/**
	 * 加密
	 * @param bytes 明文字节数组
	 * @return 密文
	 */
	@ReturnNotNull
	//	@Throws(at = "MD5.md5()", value = "NoSuchAlgorithmException", when = "MessageDigest.getInstance(String)")
	private static String encrypt(@CanNotNull final byte[] bytes) {
		return KHexUtils.bytes2hex(md5().digest(bytes));
	}

	/**
	 * 加密
	 * @param plaintext 明文
	 * @param key 辅助加密
	 * @param charset 字符集
	 * @return 密文
	 */
	//	@Throws(at = "MD5.md5()", value = "NoSuchAlgorithmException", when = "MessageDigest.getInstance(String)")
	@Throws(at = "MD5.encrypt(String, String, String)", value = "UnsupportedEncodingException", when = "String.getBytes(String)")
	@ReturnNotNull
	public static String encrypt(@CanNull(defaultValue = "") final String plaintext, @CanNull(defaultValue = "") final String key, @CanNull(defaultValue = "Configs.default_MD5_charset") final String charset) {
		if (plaintext == null) { return encrypt("", key, charset); }
		if (key == null) { return encrypt(plaintext, "", charset); }
		if (charset == null) { return encrypt(plaintext, key, Configs.default_MD5_charset); }
		try {
			return encrypt((plaintext + key).getBytes(charset));
		} catch (final UnsupportedEncodingException e) {
			throw new KRuntimeException(e);
		}
	}

	/**
	 * 加密
	 * @param is 明文输入流
	 * @return 密文
	 */
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.available()")
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.read(byte[])")
	//	@Throws(at = "MD5.md5()", value = "NoSuchAlgorithmException", when = "MessageDigest.getInstance(String)")
	@ReturnNotNull
	public static String encrypt(@CanNotNull final InputStream is) {
		final byte[] buffer = KReader.readBytes(is);
		final MessageDigest md5 = md5();
		md5.update(buffer, 0, buffer.length);
		return KHexUtils.bytes2hex(md5.digest());
	}

}
